/*
 * @Author: fuhongliang-TSJ hongliang.fu@4d-bios.com
 * @Date: 2025-09-24 10:31:56
 * @LastEditTime: 2025-09-26 16:01:18
 * @LastEditors: fuhongliang-TSJ hongliang.fu@4d-bios.com
 * @Description: 
 * @FilePath: \lc_-esp32_-s3\main\app\sntp.c
 * 立创实战派ESP32-S3开发板
 * Copyright (c) 2025 by 4D Bios, All Rights Reserved. 
 */

#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "sntp.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_netif_sntp.h"
#include "esp_sntp.h"

static const char *TAG = "sntp";

bool sntp_sync_status = 0;

#define INET6_ADDRSTRLEN 48

#define SNTP_SERVER_ALIYUN          "ntp1.aliyun.com"
#define SNTP_SERVER_TENCENT         "time2.cloud.tencent.com"
#define SNTP_SERVER_GOOGLE          "time.google.com"

#define NTP_REQUEST_PERIOD          24*60*60              /* UNIT: SECOND */

/*
 * Function:        get_current_timestamp_second
 * Descriptioin:    获取当前时间戳 (单位: 秒)
 * Input:           没有输入
 * Output:          没有输出
 * Return:          uint32_t 类型, 当前 timestatmp 值
 * Calls:           
 * Called By:       
 * Others:			
 */
uint32_t get_current_timestamp_second(void)
{
    struct timeval tv_now;
	gettimeofday(&tv_now, NULL);

    return tv_now.tv_sec;
}

/*
 * Function:        get_timestamp_in_day
 * Descriptioin:    获取当前时间戳于一天中 (单位: 秒)
 * Input:           没有输入
 * Output:          没有输出
 * Return:          uint32_t 类型, 当前 timestatmp 值于一天中 
 * Calls:           
 * Called By:       
 * Others:			
 */
uint32_t get_timestamp_in_day(void)
{
    time_t now = 0;
    struct tm timeinfo = { 0 }; 
    time(&now);
    localtime_r(&now, &timeinfo);
    return timeinfo.tm_hour * 60 * 60 + timeinfo.tm_min * 60 + timeinfo.tm_sec;
}

/**
 * @description: 获取日期字符串
 * @param {char} *str_date
 * @return {*}
 */
void get_date_str(char *str_date)
{
    time_t now = 0;
    struct tm timeinfo = { 0 }; 
    time(&now);
    localtime_r(&now, &timeinfo);

    sprintf(str_date,"%04d年%02d月%02d日", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
}

/**
 * @description: 获取sntp同步状态
 * @return {*}
 */
bool get_sntp_sync_status(void)
{
    return sntp_sync_status;
}

/**
 * @description: 等待同步sntp同步
 * @return {*}
 */
esp_err_t netif_sntp_sync(void)
{
    esp_err_t ret = ESP_OK;
    int retry = 0;
    const int retry_count = 15;

    while (esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS) == ESP_ERR_TIMEOUT && ++retry < retry_count) {
        ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
    }

    if (retry > 14)
    {
        ret = ESP_FAIL;
        ESP_LOGI(TAG, "Waiting for system time fail");
    }

    return ret;
}

/**
 * @description: 打印sntp服务器地址信息
 * @return {*}
 */
static void print_servers(void)
{
    ESP_LOGI(TAG, "List of configured NTP servers:");

    for (uint8_t i = 0; i < SNTP_MAX_SERVERS; ++i){
        if (esp_sntp_getservername(i)){
            ESP_LOGI(TAG, "server %d: %s", i, esp_sntp_getservername(i));
        } else {
            // we have either IPv4 or IPv6 address, let's print it
            char buff[INET6_ADDRSTRLEN];
            ip_addr_t const *ip = esp_sntp_getserver(i);
            if (ipaddr_ntoa_r(ip, buff, INET6_ADDRSTRLEN) != NULL)
                ESP_LOGI(TAG, "server %d: %s", i, buff);
        }
    }
}

/**
 * @description: sntp同步后回调函数
 * @param {timeval} *tv
 * @return {*}
 */
static void time_sync_notification_cb(struct timeval *tv)
{
    ESP_LOGI(TAG, "Notification of a time synchronization event");
}

/**
 * @description: 初始化sntp
 * @return {*}
 */
void initialize_sntp(void)
{
    ESP_LOGI(TAG, "Initializing and starting SNTP");
    esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(3,
                            ESP_SNTP_SERVER_LIST(SNTP_SERVER_ALIYUN, SNTP_SERVER_TENCENT, SNTP_SERVER_GOOGLE) );
    config.sync_cb = time_sync_notification_cb;     // Note: This is only needed if we want
    esp_netif_sntp_init(&config);
    print_servers();

    // Set timezone to China Standard Time
    setenv("TZ", "CST-8", 1);
    tzset();
}

/**
 * @description: SNTP任务
 * @param {void*} arg
 * @return {*}
 */
void sntp_task(void* arg)
{
    int time_counter = 0;
    time_t now;
    struct tm timeinfo;

    initialize_sntp();

    sntp_sync_status = (netif_sntp_sync() == ESP_OK) ? SNTP_SYNC_STATUS_SUCCEED : SNTP_SYNC_STATUS_FAIL;

    while(1)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        time_counter++;

        ESP_LOGD(TAG, "### sntp_sync_status: %d \n", sntp_sync_status);

        if (time_counter % 10 == 0)
        {
            time(&now);
            localtime_r(&now, &timeinfo);
            ESP_LOGD(TAG, "### Current Local time is: %s \n", asctime(&timeinfo));
        }
        
        if (time_counter > NTP_REQUEST_PERIOD)
        {
            time_counter = 0;
            esp_netif_sntp_start();
            sntp_sync_status = (netif_sntp_sync() == ESP_OK) ? SNTP_SYNC_STATUS_SUCCEED: SNTP_SYNC_STATUS_FAIL;
        }
    }

    esp_netif_sntp_deinit();
    vTaskDelete(NULL);
}

/**
 * @description: SNTP任务初始化
 * @return {*}
 */
void sntp_task_init(void)
{
     xTaskCreate(sntp_task, "sntp_task", 3 * 1024, NULL, 3, NULL);
}